home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 …ember: Reference Library / Apple Developer Reference Library (December 1999) (Disk 1).iso / pc / technical documentation / macintosh technotes and q&as / technotes / tn / samplecode.sit.hqx / Sample Code / menus & windows.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-22  |  23.3 KB  |  866 lines

  1. /*
  2.     File:        menus & windows.c
  3.  
  4.     Contains:    This file contains the menu and window handling routines for the QuickDraw
  5.                 GX shell application.
  6.  
  7.     Written by:    Developer Technical Support
  8.  
  9.     Copyright:    © 1992-1997 by Apple Computer, Inc., all rights reserved.
  10.  
  11.     Writers:
  12.  
  13.         (DMH)    Dave Hersey
  14.         (IK)    Ingrid Kelly
  15.  
  16.     Change History (most recent first):
  17.  
  18.          <6>      6/1/97    IK        Modified printing routines for GXGraphics 1.1.6.
  19.          <5>      8/1/94    DMH        Universalized.
  20.          <4>      5/1/94    DMH        Fixed some bad, bad, viewport-confused code.
  21.          <3>      9/1/93    PLA        Updated to run with the b2 "GXified" interface files.
  22.          <2>      2/1/93    DMH        Debugged and plopped on GX CD.
  23.          <1>      9/1/92    DMH        First created.
  24. */
  25.  
  26. #include <stdio.h>
  27. #include <Devices.h>
  28. #include <GXEnvironment.h>
  29.  
  30. #include "FontLibrary.h"
  31. #include "GraphicsLibraries.h"
  32. #include "PrintingLibrary.h"
  33.  
  34. #include "main.h"
  35.  
  36. /* ---------------------------------------------------------------------------
  37.     Constants
  38.   --------------------------------------------------------------------------- */
  39.  
  40. #define    kOSEvent                    app4Evt        /*    Event used by the Process Manager. */
  41. #define    kSuspendResumeMessage        1            /*    High byte of suspend/resume event message. */
  42. #define    kResumeMask                    1            /*    Bit of message field for resume event message. */
  43.  
  44. /* ---------------------------------------------------------------------------
  45.     Function prototypes
  46.   --------------------------------------------------------------------------- */
  47.  
  48. static Boolean    IsAppWindow(WindowPtr window);
  49. static void        CreateSampleImage(TH_Document document, short whichPg);
  50. static OSErr    InsertPage(TH_Document document, short *whichPg);
  51. static void        DisposePage(TH_Document document, short    whichPg);
  52.  
  53. /* ===========================================================================
  54.     Public functions
  55.   =========================================================================== */
  56.  
  57. /* ---------------------------------------------------------------------------
  58.     DoSetup
  59.     Initialize the global variables and custom data structures of the
  60.     application.
  61.   --------------------------------------------------------------------------- */
  62.  
  63. void DoSetup(void)
  64. {
  65. }
  66.  
  67. /* ---------------------------------------------------------------------------
  68.     DoDraw
  69.     Draw the contents of a window.
  70.   --------------------------------------------------------------------------- */
  71.  
  72. void DoDraw(WindowPtr window)
  73. {
  74.     if ( window != nil )
  75.     {
  76.         TH_Document    document = GetDocument(window);
  77.  
  78.         if ( document != nil )
  79.             GXDrawShape(GetPageShape(document, (*document)->page));
  80.     }
  81. }
  82.  
  83. /* ---------------------------------------------------------------------------
  84.     InitDocument
  85.     Initialize the custom data structure of a window.
  86.   --------------------------------------------------------------------------- */
  87.  
  88. OSErr InitDocument(WindowPtr window)
  89. {
  90.     TH_Document    document;
  91.     gxprJobHdl    gxprJob = nil;
  92.     short        newPage;
  93.     OSErr        error = noErr;
  94.  
  95.     /*    Allocate the custom data structure of the document. */
  96.  
  97.     document = (TH_Document)NewHandleClear(sizeof(T_Document));
  98.     if ( document == nil )
  99.     {
  100.         error = MemError();
  101.         goto NewHandleClearFailed;
  102.     }
  103.  
  104.     (*document)->window = window;
  105.     (*document)->viewport = GXNewWindowViewPort(window);            
  106.     error = (OSErr)GXGetGraphicsError(nil);
  107.  
  108.     if ( error != noErr )
  109.         goto GXNewWindowViewPortFailed;
  110.  
  111.     /*    If QuickDraw GX printing is present, create a new job object and install
  112.         an application override for the gxPrintingEvent message. Note that you
  113.         only need to have this message overridden while print dialogs are
  114.         displayed, but overriding it here doesn't hurt.
  115.         
  116.         Create a print job for this document. This will be the same as the system
  117.         default until the user goes through the dialogs for Page Setup or Print.
  118.     */
  119.     error = GXPrNewJob(&gxprJob);
  120.  
  121.     /*    If there are no errors, install our overrides for the format dialog (which
  122.         will add our panel to it) and gxPrintingEvent (which will allow us to
  123.         update our windows as the printing dialogs are moved around. Create a
  124.         handle the size of our document structure and store the print job and one
  125.         page description in it. Store the handle in the window's refCon field so
  126.         that we can get at it. Note that the utility routines "GetDocumentJob"
  127.         and "GetPageShape" can be used to do this easily.
  128.     */
  129.     if ( error == noErr )
  130.     {
  131.         GXPrInstallApplicationOverride(gxprJob, gxPrintingEventMsg, gGXPrintingEventUPP);
  132.         GXPrInstallApplicationOverride(gxprJob, gxFormatDialogMsg, gGXFormatDialogUPP);
  133.  
  134.         error = GXPrGetJobError(gxprJob);
  135.     }
  136.     else
  137.         goto GXPrNewJobFailed;
  138.  
  139.     (*document)->gxprJob = gxprJob;
  140.     (*document)->page = 1;
  141.     (*document)->pageCount = 0;
  142.  
  143.     newPage = 1;
  144.  
  145.     error = InsertPage(document, &newPage);
  146.     if ( error != noErr )
  147.         goto InsertPageFailed;
  148.  
  149.     SetDocument(window, document);
  150.     if ( error == noErr )
  151.         goto Completed;
  152.  
  153. InsertPageFailed:
  154.     if ( gxprJob != nil )
  155.         GXPrDisposeJob(gxprJob);
  156.  
  157. GXPrNewJobFailed:
  158. GXNewWindowViewPortFailed:
  159.     if ( document != nil )
  160.         DisposeHandle((Handle)document);
  161.  
  162. NewHandleClearFailed:
  163. Completed:
  164.     return error;
  165. }
  166.  
  167. /* ---------------------------------------------------------------------------
  168.     DoNew
  169.     Create a new window.
  170.   --------------------------------------------------------------------------- */
  171.  
  172. OSErr DoNew(void)
  173. {
  174.     WindowPtr    window;
  175.     OSErr        error = noErr;
  176.  
  177.     /*     Create a window for the document. */
  178.  
  179.     window = GetNewCWindow(rDocumentWINDID, nil, (WindowPtr)-1L);
  180.     if ( window == nil )
  181.     {
  182.         error = MemError();
  183.         goto GetNewCWindowFailed;
  184.     }
  185.  
  186.     /*    Create and initialize the private data for the document; attach a default
  187.         viewport to it, and add a sample image to its page shape.
  188.     */
  189.     error = InitDocument(window);
  190.     if ( error != noErr )
  191.         goto DoDocumentInitFailed;
  192.  
  193.     /*     Invalidate the window's portRect so that everything gets updated. */
  194.  
  195.     SetPort(window);
  196.     InvalRect(&window->portRect);
  197.     if ( error == noErr )
  198.         goto Completed;
  199.  
  200. DoDocumentInitFailed:
  201.     DoClose(window);
  202.  
  203. GetNewCWindowFailed:
  204. Completed:
  205.     return error;
  206. }
  207.  
  208. /* ---------------------------------------------------------------------------
  209.     DoClose
  210.     Close a window and dispose of its custom data structure.
  211.  
  212.     You should always dispose of your GX graphics objects before tossing your
  213.     window. Why? It's generally good form and this approach guarantees that
  214.     everything is disposed. If you had not disposed of everything, the call to
  215.     DisposeWindow should dispose of the objects. If you are running the
  216.     debugging version of the SecretGraphics init with notices set, you will
  217.     receive a notice that you had not disposed of everything.
  218.   --------------------------------------------------------------------------- */
  219.  
  220. void DoClose(WindowPtr window)
  221. {
  222.     short    pageCount;
  223.     short    pg;
  224.  
  225.     if ( window != nil )
  226.     {
  227.         TH_Document    document = GetDocument(window);
  228.         gxprJobHdl    gxprJob = nil;
  229.  
  230.         if ( document != nil )
  231.         {
  232.             pageCount = (*document)->pageCount;                /*  Get the number of pages in our document. */
  233.  
  234.             for ( pg = pageCount; pg >= 1; pg-- )
  235.                 DisposePage(document, pg);                     /*  Dispose of each page's info and shapes. */
  236.  
  237.             /*  Dispose of this document's print job. */
  238.  
  239.             gxprJob = GetDocumentJob(document);
  240.             if ( gxprJob != nil )
  241.                 GXPrDisposeJob(gxprJob);
  242.  
  243.             DisposeHandle((Handle)document);                /*  Dispose of the document's private data. */
  244.                DisposeWindow(window);                            /*  Dispose of the window. */
  245.         }
  246.     }
  247. }
  248.  
  249. /* ---------------------------------------------------------------------------
  250.     DoIdle
  251.     Handle idle event tasks from the event loop.
  252.   --------------------------------------------------------------------------- */
  253.  
  254. void DoIdle(WindowPtr window)
  255. {
  256. }
  257.  
  258. /* ---------------------------------------------------------------------------
  259.     DoTeardown
  260.     Dispose of the custom data structures of the application that were created
  261.     in DoSetup().
  262.   --------------------------------------------------------------------------- */
  263.  
  264. void DoTeardown(void)
  265. {
  266. }
  267.  
  268. /* ---------------------------------------------------------------------------
  269.     DoClick
  270.   --------------------------------------------------------------------------- */
  271.  
  272. void DoClick(WindowPtr window, Point p)
  273. {
  274. }
  275.  
  276. /* ---------------------------------------------------------------------------
  277.     DoEvent
  278.   --------------------------------------------------------------------------- */
  279.  
  280. void DoEvent(EventRecord *event)
  281. {
  282.     char            key;
  283.     WindowPtr         window;
  284.     GrafPtr            oldPort;
  285.     Rect            screenRect;
  286.  
  287.     switch( event->what )
  288.     {                    
  289.         case updateEvt:
  290.             if ( IsAppWindow((WindowPtr)event->message) )
  291.             {
  292.                 GetPort(&oldPort);
  293.                 SetPort((WindowPtr)event->message);
  294.                 BeginUpdate((WindowPtr)event->message);
  295.                 DoDraw((WindowPtr)event->message);
  296.                 EndUpdate((WindowPtr)event->message);
  297.                 SetPort(oldPort);
  298.             }
  299.             break;
  300.  
  301.         case mouseDown:
  302.             switch ( FindWindow(event->where, &window) )
  303.             {
  304.                 case inSysWindow:
  305.                     SystemClick(event, window);
  306.                 break;
  307.                         
  308.                 case inDrag:
  309.                     screenRect = (**GetGrayRgn()).rgnBBox;
  310.                     if ( IsAppWindow(window) )
  311.                         DragWindow(window, event->where, &screenRect);
  312.                 break;
  313.                         
  314.                 case inGoAway:
  315.                     if ( IsAppWindow(window) )
  316.                         if ( TrackGoAway(window, event->where) )
  317.                             DoClose(window);
  318.                 break;
  319.                         
  320.                 case inContent:
  321.                     if ( IsAppWindow(window) )
  322.                         if ( window == FrontWindow() )
  323.                             DoClick(window, event->where);
  324.                         else
  325.                             SelectWindow(window);
  326.                 break;
  327.                         
  328.                 case inMenuBar:
  329.                     DoMenuCommand(MenuSelect(event->where));
  330.                 break;
  331.             }
  332.             break;
  333.  
  334.         case keyDown:
  335.         case autoKey:
  336.             key = event->message & charCodeMask;
  337.             if ( event->modifiers & cmdKey )
  338.                 if ( event->what == keyDown )
  339.                     DoMenuCommand(MenuKey(key));
  340.             break;
  341.  
  342.         case kOSEvent:
  343.             switch ( (unsigned long)event->message >> 24 )    /*  high byte of message */
  344.             {
  345.                 /*  The Suspend/Resume event is also an Activate/Deactivate event. */
  346.  
  347.                  case kSuspendResumeMessage:
  348.                     if ( (event->message & kResumeMask ) == 0 )
  349.                        gSleep = 80;        /*  The application is headed for the background, so slow down. */
  350.                     else
  351.                     {
  352.                        gSleep = 0;        /*  The application is headed for the foreground, so speed up. */
  353.  
  354.                         /*    On a resume event, we need to call GXUpdateJob on all of our documents'
  355.                             jobs. This is important because the user may have just changed something
  356.                             which affects our jobs (like the size of the paper in the printer).
  357.                             
  358.                             Since our application stores our document references in the refCon fields
  359.                             of our documents' windows, we just loop through every one of our windows,
  360.                             extract our document pointers and update the associated jobs.
  361.                         */
  362.                         window = FrontWindow();
  363.  
  364.                         while ( window != nil )
  365.                         {
  366.                             if ( ((WindowPeek)window)->windowKind == userKind )
  367.                                 GXPrUpdateJob(GetDocumentJob(GetDocument(window)));
  368.                             
  369.                             window = (WindowPtr)((WindowPeek)window)->nextWindow;
  370.                         }
  371.                     }
  372.                     break;
  373.             }
  374.             break;
  375.     }
  376. }
  377.  
  378. /* ---------------------------------------------------------------------------
  379.     DoMenuCommand
  380.     Handle menu event tasks from the event loop.
  381.   --------------------------------------------------------------------------- */
  382.  
  383. void DoMenuCommand(long menuResult)
  384. {
  385.     short                menuID;
  386.     short                menuItem;
  387.     Str255                daName;
  388.     WindowPtr            activeWindow;
  389.     TH_Document            activeDocument;
  390.     gxDialogResult        result;
  391.     short                page;
  392.     short                pageCount;
  393.     short                newPage;
  394.     OSErr                error = noErr;
  395.  
  396.     menuID = menuResult >>16;
  397.     menuItem = menuResult & 0x0000ffff;
  398.     activeWindow = FrontWindow();
  399.     activeDocument = GetDocument(activeWindow);
  400.  
  401.     switch ( menuID )
  402.     {
  403.         case mApple:
  404.             switch ( menuItem )
  405.             {
  406.                 case iAbout:
  407.                     /*    Display the About box. */
  408.                     Alert(rAboutBoxALRTID, nil);
  409.                     break;
  410.                 
  411.                 default:
  412.                     /*    Handle the Apple menu. */
  413.                     GetItem(GetMHandle(mApple), menuItem, daName);
  414.                     OpenDeskAcc(daName);
  415.                     break;
  416.             }
  417.             break;
  418.             
  419.             case mFile:
  420.                 switch ( menuItem )
  421.                 {                        
  422.                     case iNew:
  423.                         /*    Create a new document. */
  424.                         error = DoNew();
  425.                         break;
  426.                                                 
  427.                     case iOpen:
  428.                         /*    Open a document. */
  429.                         break;
  430.                                                 
  431.                     case iClose:
  432.                         /*    Close the current document. */
  433.                         DoClose(activeWindow);
  434.                         break;
  435.                                                 
  436.                     case iSave:
  437.                         /*    Save the current document. */
  438.                         break;
  439.  
  440.                     case iPageSetup:
  441.                         /*    Set the default page format of the current document. */
  442.                         HiliteMenu(0);
  443.                         error = DoPageFormat(activeWindow, &result);
  444.                         break;
  445.                                                 
  446.                     case iCustomPageSetup:
  447.                         /*    Set the current page format. */
  448.                         HiliteMenu(0);
  449.                         error = DoCustomPageFormat(activeWindow, &result);
  450.                         break;
  451.                                                 
  452.                     case iPrint:
  453.                         /*    Print the current document. */
  454.                         HiliteMenu(0);
  455.                         error = DoPrint(activeWindow);
  456.                         break;
  457.  
  458.                     case iPrintOneCopy:
  459.                         /*    Print one copy of the current document. */
  460.                         error = DoPrintOneCopy(activeWindow);
  461.                         break;
  462.                         
  463.                     case iQuit:
  464.                         gQuitting = true;
  465.                         break;
  466.                 }
  467.                 break;
  468.                 
  469.             case mEdit:
  470.                 break;
  471.                 
  472.             case mDocument:
  473.                 switch ( menuItem )
  474.                 {                        
  475.                     case iInsertPage:
  476.                             /*    Add a page */
  477.                             newPage = (*activeDocument)->page;
  478.                             InsertPage(activeDocument, &newPage);
  479.                             (*activeDocument)->page = newPage;
  480.  
  481.                             SetPort(activeWindow);
  482.                             EraseRect(&activeWindow->portRect);
  483.                             InvalRect(&activeWindow->portRect);
  484.                         break;
  485.                                                 
  486.                     case iDeletePage:
  487.                             /*    Delete the active page. */
  488.                             page = (*activeDocument)->page;
  489.                             pageCount = (*activeDocument)->pageCount;
  490.                             if ( pageCount > 1 )
  491.                             {
  492.                                 DisposePage(activeDocument, page);
  493.  
  494.                                 if ( page == pageCount )
  495.                                     page = --(*activeDocument)->page;
  496.  
  497.                                 SetPort(activeWindow);
  498.                                 EraseRect(&activeWindow->portRect);
  499.                                 InvalRect(&activeWindow->portRect);
  500.                             }
  501.                         break;
  502.                                                 
  503.                     case iNextPage:
  504.                             /*    Move to the next page. */
  505.                             page = (*activeDocument)->page;
  506.                             pageCount = (*activeDocument)->pageCount;
  507.                             if ( page < pageCount )
  508.                             {
  509.                                 ++(*activeDocument)->page;
  510.  
  511.                                 SetPort(activeWindow);
  512.                                 EraseRect(&activeWindow->portRect);
  513.                                 InvalRect(&activeWindow->portRect);
  514.                             }
  515.                         break;
  516.                                                 
  517.                     case iPrevPage:
  518.                             /*    Move to the previous page. */
  519.                             page = (*activeDocument)->page;
  520.                             if ( page > 1 )
  521.                             {
  522.                                 --(*activeDocument)->page;
  523.  
  524.                                 SetPort(activeWindow);
  525.                                 EraseRect(&activeWindow->portRect);
  526.                                 InvalRect(&activeWindow->portRect);
  527.                             }
  528.                         break;
  529.                 }
  530.                 break;
  531.     }
  532.  
  533.     HiliteMenu(0);
  534. }
  535.  
  536. /* ---------------------------------------------------------------------------
  537.     EventLoop
  538.   --------------------------------------------------------------------------- */
  539.  
  540. void EventLoop(void)
  541. {
  542.     EventRecord        event;
  543.  
  544.     if ( WaitNextEvent(everyEvent, &event, gSleep, nil) )
  545.         DoEvent(&event);
  546.     else
  547.         DoIdle(FrontWindow());
  548. }
  549.  
  550. /* ---------------------------------------------------------------------------
  551.     Accessor routines for the document structure attached to a window.
  552.   --------------------------------------------------------------------------- */
  553.  
  554. TH_Document GetDocument(WindowPtr window)
  555. {
  556.     TH_Document    document = nil;
  557.  
  558.     if ( window )
  559.         document = (TH_Document)GetWRefCon(window);
  560.     return document;
  561. }
  562.  
  563. void SetDocument(WindowPtr window, TH_Document document)
  564. {
  565.     if ( window )
  566.         SetWRefCon(window, (long)document);
  567. }
  568.  
  569. /* ---------------------------------------------------------------------------
  570.     Accessor routines for the viewport attached to a document.
  571.   --------------------------------------------------------------------------- */
  572.  
  573. gxViewPort GetDocumentViewPort(TH_Document document)
  574. {
  575.     gxViewPort    viewport = nil;
  576.  
  577.     if ( document )
  578.         viewport = (*document)->viewport;
  579.     return viewport;
  580. }
  581.  
  582. void SetDocumentViewPort(TH_Document document, gxViewPort viewport)
  583. {
  584.     if ( document )
  585.         (*document)->viewport = viewport;
  586. }
  587.  
  588. /* ---------------------------------------------------------------------------
  589.     Accessor routines for the print job structure attached to a document.
  590.   --------------------------------------------------------------------------- */
  591.  
  592. gxprJobHdl GetDocumentJob(TH_Document document)
  593. {
  594.     gxprJobHdl    gxprJob = nil;
  595.  
  596.     if ( document )
  597.         gxprJob = (*document)->gxprJob;
  598.     return gxprJob;
  599. }
  600.  
  601. void SetDocumentJob(TH_Document document, gxprJobHdl gxprJob)
  602. {
  603.     if ( document )
  604.         (*document)->gxprJob = gxprJob;
  605. }
  606.  
  607. /* ---------------------------------------------------------------------------
  608.     Accessor routines for the page structure(s) attached to a document.
  609.   --------------------------------------------------------------------------- */
  610.  
  611. TH_Page GetDocumentPage(TH_Document document, short whichPg)
  612. {
  613.     TH_Page    page = nil;
  614.  
  615.     if ( document )
  616.         page = (*document)->pageArray[whichPg - 1];
  617.     return page;
  618. }
  619.  
  620. void SetDocumentPage(TH_Document document, short whichPg, TH_Page page)
  621. {
  622.     if ( document )
  623.         (*document)->pageArray[whichPg - 1] = page;
  624. }
  625.  
  626. /* ---------------------------------------------------------------------------
  627.     Accessor routines for the page format(s) attached to a document.
  628.   --------------------------------------------------------------------------- */
  629.  
  630. gxprFormatHdl GetPageFormat(TH_Document document, short whichPg)
  631. {
  632.     TH_Page            page;
  633.     gxprFormatHdl    gxprFormat = nil;
  634.  
  635.     if ( document )
  636.     {
  637.         page = (*document)->pageArray[whichPg - 1];
  638.         if ( page )
  639.             gxprFormat = (*page)->gxprFormat;
  640.     }
  641.     return gxprFormat;
  642. }
  643.  
  644. void SetPageFormat(TH_Document document, short whichPg, gxprFormatHdl gxprFormat)
  645. {
  646.     TH_Page        page;
  647.  
  648.     if ( document )
  649.     {
  650.         page = (*document)->pageArray[whichPg - 1];
  651.         if ( page )
  652.             (*page)->gxprFormat = gxprFormat;
  653.     }
  654. }
  655.  
  656. /* ---------------------------------------------------------------------------
  657.     Accessor routines for the page shape(s) attached to a document.
  658.   --------------------------------------------------------------------------- */
  659.  
  660. gxShape GetPageShape(TH_Document document, short whichPg)
  661. {
  662.     TH_Page        page;
  663.     gxShape        shape = nil;
  664.  
  665.     if ( document )
  666.     {
  667.         page = (*document)->pageArray[whichPg -1];
  668.         if ( page )
  669.             shape = (*page)->shape;
  670.     }
  671.     return shape;
  672. }
  673.  
  674. void SetPageShape(TH_Document document, short whichPg, gxShape shape)
  675. {
  676.     TH_Page        page;
  677.  
  678.     if ( document )
  679.     {
  680.         page = (*document)->pageArray[whichPg - 1];
  681.         if ( page )
  682.             (*page)->shape = shape;
  683.     }
  684. }
  685.  
  686. /* ===========================================================================
  687.     Private functions
  688.   =========================================================================== */
  689.  
  690. /* ---------------------------------------------------------------------------
  691.     IsAppWindow
  692.   --------------------------------------------------------------------------- */
  693.  
  694. static Boolean IsAppWindow(WindowPtr window)
  695. {
  696.     return window != nil && ((WindowPeek)window)->windowKind == userKind;
  697. }
  698.  
  699. /* ---------------------------------------------------------------------------
  700.     CreateSampleImage
  701.     This function adds a simple shape to the page indicated in the passed
  702.     document.
  703.   --------------------------------------------------------------------------- */
  704.  
  705. static void CreateSampleImage(TH_Document document, short whichPg)
  706. {
  707.     short            pageCount;
  708.     gxShape         shape;
  709.     gxShape            theText;
  710.     gxColor         textColor;
  711.     Str255            textStr;
  712.     short            charCount;
  713.     short            loop;
  714.         
  715.     pageCount = (*document)->pageCount;
  716.  
  717.     /*     Retrieve the page gxShape so we can add to it. */
  718.  
  719.     shape = GetPageShape(document, whichPg);
  720.  
  721.     charCount = sprintf((Ptr)&textStr[0], "%s %i %s", "This was added when there were", pageCount - 1, "pages");
  722.     theText = GXNewText(charCount, (unsigned char *)textStr,  nil);
  723.     SetShapeCommonFont(theText, timesFont);
  724.     GXSetShapeTextSize(theText, ff(30));
  725.     GXMoveShapeTo(theText, ff(10), ff(30));
  726.  
  727.     /*     Create an hsv gxColor space */
  728.  
  729.     textColor.space = gxHSVSpace;
  730.     textColor.profile = nil;
  731.     textColor.element.hsv.hue = 0x7400;
  732.     textColor.element.hsv.saturation = 0xFFFF;
  733.     textColor.element.hsv.value = 0xFFFF;
  734.  
  735.     for ( loop = 1; loop < 14; loop++ )
  736.     {
  737.         GXSetShapeColor(theText, &textColor);
  738.         AddToShape(shape, theText);
  739.         if ( loop < 7 )
  740.             GXMoveShape(theText, ff(10), ff(35));
  741.         else
  742.             GXMoveShape(theText, ff(-10), ff(35));
  743.         textColor.element.hsv.hue += 0x0800;
  744.     }
  745.  
  746.     GXDisposeShape(theText); 
  747. }
  748.  
  749. /* ---------------------------------------------------------------------------
  750.     InsertPage
  751.     This routine is called when a page needs to be added to a document. The
  752.     page is added AFTER the page number passed. If whichPg is greater than
  753.     the last page, the new page is added to the end of the document. If
  754.     whichPg is less than 1, the page is added to the beginning of the
  755.     document. The page number of the new page is returned in whichPg.
  756.   --------------------------------------------------------------------------- */
  757.  
  758. static OSErr InsertPage(TH_Document document, short *whichPg)
  759. {
  760.     gxprJobHdl    gxprJob = nil;
  761.     gxShape        shape = nil;
  762.     TH_Page        page;
  763.     short        pageCount;
  764.     short        aPage;
  765.     long        newSize;
  766.     OSErr        error = noErr;
  767.  
  768.     pageCount = (*document)->pageCount;
  769.  
  770.     /*     Pin the page number to insert after to the range (0, pageCount). */
  771.  
  772.     if ( *whichPg < 1 )
  773.         *whichPg = 0;
  774.     else if ( *whichPg > pageCount )
  775.         *whichPg = pageCount;
  776.  
  777.     /*    Create the page gxShape. We set the unique items attribute to make sure
  778.         that each item added to the picture has a unique reference.
  779.     */
  780.     shape = GXNewShape(gxPictureType);
  781.     GXSetShapeViewPorts(shape, 1, &(*document)->viewport);
  782.     GXSetShapeAttributes(shape, GXGetShapeAttributes(shape) | gxUniqueItemsShape);
  783.     
  784.     page = (TH_Page)NewHandleClear(sizeof(T_Page));
  785.  
  786.     if ( page == nil )
  787.         error = MemError();
  788.     else
  789.     {
  790.         newSize = GetHandleSize((Handle)document) + sizeof(T_Page);
  791.         SetHandleSize((Handle)document, newSize);
  792.         error = MemError();
  793.         
  794.         if ( error == noErr )
  795.         {
  796.             (*page)->shape = shape;
  797.             pageCount = ++(*document)->pageCount;
  798.             ++*whichPg;
  799.  
  800.             if ( *whichPg < pageCount && pageCount > 1 )
  801.                 for (aPage = pageCount; aPage > *whichPg; aPage--)
  802.                     (*document)->pageArray[aPage - 1] = (*document)->pageArray[aPage - 2];
  803.  
  804.             (*document)->pageArray[*whichPg - 1] = page;
  805.             CreateSampleImage(document, *whichPg);
  806.         }
  807.     }
  808.     
  809.     return error;
  810. }
  811.  
  812. /* ---------------------------------------------------------------------------
  813.     DisposePage
  814.     This routine is called when a page in a document needs to be disposed of.
  815.     It's smart enough to allow you to delete a page from the middle of the
  816.     document, not just from the end.
  817.   --------------------------------------------------------------------------- */
  818.  
  819. static void DisposePage(TH_Document document, short whichPg)
  820. {
  821.     TH_Page            page;
  822.     gxShape            shape;
  823.     gxprFormatHdl    gxprFormat;
  824.     short            pageCount;
  825.     short            pageIndex;
  826.     long            newSize;
  827.  
  828.     if ( whichPg > (*document)->pageCount )
  829.         return;                                            /*  ?!  There aren't that many pages!! */
  830.     if ( whichPg < 1 )
  831.         return;                                            /*  ?!  Page numbers start at 1!! */
  832.  
  833.     /*  Dispose of this page's shape. */
  834.  
  835.     shape = GetPageShape(document, whichPg);
  836.     if ( shape != nil )
  837.         GXDisposeShape(shape);
  838.  
  839.     /*  Dispose of this page's format. */
  840.  
  841.     gxprFormat = GetPageFormat(document, whichPg);
  842.     if ( gxprFormat != nil )
  843.         GXPrDisposeFormat(gxprFormat);
  844.  
  845.     page = GetDocumentPage(document, whichPg);
  846.     if ( page != nil )
  847.         DisposeHandle((Handle)page);                    /*  Dispose of this page's private data. */
  848.  
  849.     pageCount = --(*document)->pageCount;                /*  Reduce the number of pages in the document. */
  850.  
  851.     /*    If the page we deleted was not at the end of the document, we need to move
  852.         all following pages down one. In other words, if page 5 is deleted, page 6
  853.         becomes page 5, page 7 becomes page 6 and so forth. When done, resize the
  854.         handle to the document's info, since we no longer need the room occupied
  855.         by the deleted page's info.
  856.     */
  857.  
  858.     if ( whichPg <= pageCount )
  859.     {
  860.         for ( pageIndex = whichPg; pageIndex <= pageCount + 1; pageIndex++ )
  861.             (*document)->pageArray[pageIndex - 1] = (*document)->pageArray[pageIndex];
  862.  
  863.         newSize = GetHandleSize((Handle)document) - sizeof(T_Page);
  864.         SetHandleSize((Handle)document, newSize);
  865.     }
  866. }